
package l1j.opqlo.Server.L1World;

import static l1j.server.server.model.skill.L1SkillId.INVISIBILITY;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import l1j.server.Config;
import l1j.server.server.datatables.CharacterTable;
import l1j.server.server.datatables.MapsTable;
import l1j.server.server.model.L1Character;
import l1j.server.server.model.L1GroundInventory;
import l1j.server.server.model.L1Location;
import l1j.server.server.model.L1Object;
import l1j.server.server.model.L1TradeInventory;
import l1j.server.server.model.Instance.L1DollInstance;
import l1j.server.server.model.Instance.L1DoorInstance;
import l1j.server.server.model.Instance.L1DummyInstance;
import l1j.server.server.model.Instance.L1EffectInstance;
import l1j.server.server.model.Instance.L1FieldObjectInstance;
import l1j.server.server.model.Instance.L1ItemInstance;
import l1j.server.server.model.Instance.L1MonsterInstance;
import l1j.server.server.model.Instance.L1NpcInstance;
import l1j.server.server.model.Instance.L1PcInstance;
import l1j.server.server.model.Instance.L1PetInstance;
import l1j.server.server.model.Instance.L1SummonInstance;
import l1j.server.server.model.map.L1Map;
import l1j.server.server.serverpackets.S_SystemMessage;
import l1j.server.server.serverpackets.ServerBasePacket;
import l1j.server.server.types.Point;
import l1j.server.server.utils.collections.Lists;
/**
 * 遊戲世界儲存中心
 * @author dexc
 *
 */
public class L1World {
	private static Logger _log = Logger.getLogger(L1World.class.getName());
	public static L1World getInstance() {
		if (_instance == null) {
			_instance = new L1World();
		}
		return _instance;
	}
	// 世界人物資料<pcName, Object>
	private final ConcurrentHashMap<String, L1PcInstance> _allPlayers;
	//世界GM資料
	private final ConcurrentHashMap<Integer, L1PcInstance> _allGms;
	private Collection<L1PcInstance> _allGmz;
	
	// 世界物件資料<Objid, Object>
	private final ConcurrentHashMap<Integer, L1Object> _allObjects;

	// 世界物件資料(區分地圖編號)<MapId, <Objid, Object>>
	private final HashMap<Integer, ConcurrentHashMap<Integer, L1Object>> _visibleObjects;


	private final ConcurrentHashMap<Integer , L1TradeInventory> _trade_inventory;

	private final ConcurrentHashMap<Integer ,  Integer> _killList ;

	private int _dummyAmount;
	private final List<L1DummyInstance> _dummy;

	private int _weather = 4;

	private boolean _worldChatEnabled = true;

	//private static final int MAX_MAP_ID = 10000;

	private boolean _processingContributionTotal = false;

	private static L1World _instance;

	// _allObjects 的 Collection
	private Collection<L1Object> _allValues;

	// 全部玩家
	private Collection<L1PcInstance> _allPlayerValues;
	private L1World() {
		this._allPlayers = new ConcurrentHashMap<String, L1PcInstance>(); // 全てのプレイヤー
		this._allGms = new ConcurrentHashMap<Integer, L1PcInstance>();
		this._allObjects = new ConcurrentHashMap<Integer, L1Object>(); // 全てのオブジェクト(L1ItemInstance入り、L1Inventoryはなし)
		this._visibleObjects = new HashMap<Integer, ConcurrentHashMap<Integer, L1Object>>(); // 世界物件資料(區分地圖編號)
		this._trade_inventory = new ConcurrentHashMap<Integer , L1TradeInventory>();//世界交易背包
		//_visibleObjects = new ConcurrentHashMap[MAX_MAP_ID + 1]; // マップ毎のオブジェクト(L1Inventory入り、L1ItemInstanceはなし)
		this._killList = new ConcurrentHashMap<Integer , Integer>();
		this._dummy = Lists.newList();
		for (final Integer mapid : MapsTable.getInstance().getMaps().keySet()) {
			final ConcurrentHashMap<Integer, L1Object> map =new ConcurrentHashMap<Integer, L1Object>();
			this._visibleObjects.put(mapid, map);
		}
		/*for (int i = 0; i <= MAX_MAP_ID; i++) {
			_visibleObjects[i] = new ConcurrentHashMap<Integer, L1Object>();
		}*/
	}

	/**取得殺手ID*/
	public int getKillerId(final int objid){
		final Integer killerid = this._killList.get(objid);
		if(killerid == null){
			return 0;
		}
		return killerid;
	}
	/**寫入殺手ID*/
	public void putKillerId(final int objid , final int attackerId){
		if(objid <=0 
				|| attackerId<=0 
				|| this._killList.containsKey(objid) == false
				|| this._killList.get(objid) == attackerId){
			return;
		}
		this._killList.put(objid, attackerId);
	}
	/**清除殺手ID*/
	public void removeKillerId(final int objid){
		this._killList.remove(objid);
	}
	/**清除殺手ID(殺手更名後清除處理)*/
	public void removeKillerIdForChangeName(final int objid){
		removeKillerId(objid);
		if(this._killList.containsValue(objid)){
			final Set<Integer>set = this._killList.keySet();
			Iterator<Integer> iter = set.iterator();
			while(iter.hasNext()){
				final int defobjid = iter.next();
				if(this._killList.get(defobjid)==objid){
					this._killList.remove(defobjid);
					final L1PcInstance tgpc = (L1PcInstance) this.findObject(defobjid);
					if(tgpc != null){
						tgpc.removeKillerId();
						tgpc.sendPackets(new S_SystemMessage(CharacterTable.getInstance().getPcNameById(objid)+" 改名或服刑清除所有仇殺紀錄。"));
					}
				}
			}
		}
	}

	/**
	 * 新增交易背包
	 *
	 * @param L1PcInstance
	 */
	public L1TradeInventory addTradeInventory(final int pc_objid){
		L1TradeInventory inv = this._trade_inventory.get(pc_objid);
		if(inv == null){
			inv = new L1TradeInventory(pc_objid);
			this._trade_inventory.put(new Integer(pc_objid), inv);
		}
		return inv;
	}
	/**
	 * 加入MAP內物件
	 * @param object
	 */
	public void addVisibleObject(final L1Object object) {
		final ConcurrentHashMap<Integer, L1Object> map = this._visibleObjects.get(new Integer(object.getMapId()));
		if (map != null) {
			map.put(object.getId(), object);
		} else {
			//_log.error("遊戲世界儲存中心並未建立該地圖編號資料檔案: " + object.getMapId());
		}
	}
	/**
	 * 送出全體封包
	 * 配合關電視指令
	 * @param playe ,packet
	 */
	public void broadcastPacketToAll(L1PcInstance player ,ServerBasePacket packet) {
		_log.finest("players to notify : " + this.getAllPlayers().size());
		for (final L1PcInstance pc : this.getAllPlayers()) {
			if(!pc.getConfig().isTVon() && player != pc){
				continue;
			}
			pc.sendPackets(packet);
		}
	}
	/**
	 * 送出全體訊息
	 * 配合關電視指令
	 * @param message
	 */
	public void broadcastPacketToAll(final String message) {
		_log.finest("players to notify : " + this.getAllPlayers().size());
		final ServerBasePacket packet = new S_SystemMessage(message);
		for (final L1PcInstance pc : this.getAllPlayers()) {
			if(!pc.getConfig().isTVon() ){
				continue;
			}
			pc.sendPackets(packet);
		}
	}

	/**
	 * 送出全體封包
	 *
	 * @param packet
	 */
	public void broadcastPacketToAll(ServerBasePacket packet) {
		_log.finest("players to notify : " + this.getAllPlayers().size());
		for (final L1PcInstance pc : this.getAllPlayers()) {
			pc.sendPackets(packet);
		}
	}
	/**
	 * 送出全體訊息封包
	 *
	 * @param message
	 */
	public void broadcastServerMessage(final String message) {
		this.broadcastPacketToAll(new S_SystemMessage(message));
	}
	/**
/**
	 * 世界資料狀態全部重置
	 */
	public void clear() {
		_instance = new L1World();
	}
	/**
	 * 直線上目標列舉
	 * @param src
	 * @param target
	 * @return
	 */
	private ConcurrentHashMap<Integer, Integer> createLineMap(final Point src,final Point target) {
		final ConcurrentHashMap<Integer, Integer> lineMap = new ConcurrentHashMap<Integer, Integer>();

		/*
		 * http://www2.starcat.ne.jp/~fussy/algo/algo1-1.htmより
		 */
		int E;
		int x;
		int y;
		int key;
		int i;
		final int x0 = src.getX();
		final int y0 = src.getY();
		final int x1 = target.getX();
		final int y1 = target.getY();
		final int sx = x1 > x0 ? 1 : -1;
		final int dx = x1 > x0 ? x1 - x0 : x0 - x1;
		final int sy = y1 > y0 ? 1 : -1;
		final int dy = y1 > y0 ? y1 - y0 : y0 - y1;

		x = x0;
		y = y0;
		/* 傾きが1以下の場合 */
		if (dx >= dy) {
			E = -dx;
			for (i = 0; i <= dx; i++) {
				key = (x << 16) + y;
				lineMap.put(key, key);
				x += sx;
				E += 2 * dy;
				if (E >= 0) {
					y += sy;
					E -= 2 * dx;
				}
			}
			/* 傾きが1より大きい場合 */
		} else {
			E = -dy;
			for (i = 0; i <= dy; i++) {
				key = (x << 16) + y;
				lineMap.put(key, key);
				y += sy;
				E += 2 * dx;
				if (E >= 0) {
					x += sx;
					E -= 2 * dy;
				}
			}
		}

		return lineMap;
	}

	/**
	 * 取回物件
	 * @param oID
	 * @return
	 */
	public L1Object findObject(final int oID) {
		return this._allObjects.get(oID);
	}

	/**取回線上GM清單*/
	public Collection<L1PcInstance> getAllGms() {
		try {
			final Collection<L1PcInstance> vs = this._allGmz;
			return vs != null ? vs : (this._allGmz = Collections.unmodifiableCollection(this._allGms.values()));
			//return Collections.unmodifiableCollection(_isCrown.values());

		} catch (final Exception e) {
			_log.log(Level.SEVERE ,e.getLocalizedMessage(), e);
		}
		return null;
	}
	/**
	 * 全部玩家
	 * @return
	 */
	public Collection<L1PcInstance> getAllPlayers() {
		final Collection<L1PcInstance> vs = this._allPlayerValues;
		return vs != null ? vs : (this._allPlayerValues = Collections.unmodifiableCollection(this._allPlayers.values()));
	}
	/**
	 * 取回全部世界物件
	 * @return
	 */
	public final Map<Integer, L1Object> getAllVisibleObjects() {
		return this._allObjects;
	}

	/**
	 * 座標點-遊戲世界地面背包
	 * @param x
	 * @param y
	 * @param map
	 * @return
	 */
	public L1GroundInventory getInventory(final int x, final int y, final short map) {
		// 以XY座標值做為地面背包OBJID(避免衝突 轉為負數)
		final int inventoryKey = ((x - 30000) * 10000 + y - 30000) * -1;
		final ConcurrentHashMap<Integer, L1Object> idmap = this._visibleObjects.get(new Integer(map));
		if (idmap != null) {
			final Object object = idmap.get(inventoryKey);
			if (object == null) {
				return new L1GroundInventory(inventoryKey, x, y, map);
			} else {
				return (L1GroundInventory) object;
			}
		}
		return null;

	}
	/**
	 * 座標點-遊戲世界地面背包
	 * @param loc
	 * @return
	 */
	public L1GroundInventory getInventory(final L1Location loc) {
		return this.getInventory(loc.getX(), loc.getY(), (short) loc.getMap().getId());
	}
	/**
	 * 全部L1Object
	 * @return
	 */
	public Collection<L1Object> getObject() {
		final Collection<L1Object> vs = this._allValues;
		return vs != null ? vs : (this._allValues = Collections
				.unmodifiableCollection(this._allObjects.values()));
	}
	/**
	 * 以人物名稱傳回人物數據
	 * @param name 人物名稱(不區分大小寫)
	 * @return
	 */
	public L1PcInstance getPlayer(final String name) {
		if (this._allPlayers.containsKey(name)) {
			return this._allPlayers.get(name);
		}
		for (final L1PcInstance each : this.getAllPlayers()) {
			if (each.getName().equalsIgnoreCase(name)) {
				return each;
			}
		}
		return null;
	}

	/**
	 * object 畫面內可見範圍物件取回
	 *
	 * @param object
	 * @return
	 */
	public ArrayList<L1PcInstance> getRecognizePlayer(final L1Object object) {
		return this.getVisiblePlayer(object, Config.PC_RECOGNIZE_RANGE);
	}


	public Object getRegion(final Object object) {
		return null;
	}
	/**
	 * 取得交易背包
	 *
	 * @param L1PcInstance
	 */
	public L1TradeInventory getTradeInventory(final int pc_objid){
		return this._trade_inventory.get(pc_objid);
	}
	/**
	 * 指定範圍目標列舉
	 * @param object
	 * @param heading
	 * @param width
	 * @param height
	 * @return
	 */
	public ArrayList<L1Object> getVisibleBoxObjects(final L1Object object,
			final int heading, final int width, final int height) {
		final int x = object.getX();
		final int y = object.getY();
		final int map = object.getMapId();
		final L1Location location = object.getLocation();
		final ArrayList<L1Object> result = new ArrayList<L1Object>();
		final int headingRotate[] = { 6, 7, 0, 1, 2, 3, 4, 5 };
		final double cosSita = Math.cos(headingRotate[heading] * Math.PI / 4);
		final double sinSita = Math.sin(headingRotate[heading] * Math.PI / 4);

		// 取回原地圖資料
		final ConcurrentHashMap<Integer, L1Object> mapSrc =
				this._visibleObjects.get(new Integer(map));
		if (mapSrc == null) {
			//	_log.error("遊戲世界儲存中心並未建立該地圖編號資料檔案: " + map);
			return result;
		}

		for (final L1Object element : mapSrc.values()) {
			if (element.equals(object)) {
				continue;
			}
			if (element.equals(object)) {
				continue;
			}
			if (map != element.getMapId()) {
				continue;
			}

			// 同じ座標に重なっている場合は範囲内とする
			if (location.isSamePoint(element.getLocation())) {
				result.add(element);
				continue;
			}

			final int distance = location.getTileLineDistance(element
					.getLocation());
			// 直線距離が高さ、幅どちらよりも大きい場合、計算するまでもなく範囲外
			if (distance > height && distance > width) {
				continue;
			}

			// objectの位置を原点とするための座標補正
			final int x1 = element.getX() - x;
			final int y1 = element.getY() - y;

			// Z軸回転させ角度を0度にする。
			final int rotX = (int) Math.round(x1 * cosSita + y1 * sinSita);
			final int rotY = (int) Math.round(-x1 * sinSita + y1 * cosSita);

			final int xmin = 0;
			final int xmax = height;
			final int ymin = -width;
			final int ymax = width;

			// 奥行きが射程とかみ合わないので直線距離で判定するように変更。
			// if (rotX > xmin && rotX <= xmax && rotY >= ymin && rotY <=
			// ymax) {
			if (rotX > xmin && distance <= xmax && rotY >= ymin
					&& rotY <= ymax) {
				result.add(element);
			}
		}

		return result;
	}
	/**
	 * 直線距離目標列舉
	 * @param src
	 * @param target
	 * @return
	 */
	public ArrayList<L1Object> getVisibleLineObjects(final L1Object src,final L1Object target) {
		final ConcurrentHashMap<Integer, Integer> lineMap = this.createLineMap(src.getLocation(), target.getLocation());

		final int map = target.getMapId();
		final ArrayList<L1Object> result = new ArrayList<L1Object>();

		// 取回原地圖資料
		final ConcurrentHashMap<Integer, L1Object> mapSrc =
				this._visibleObjects.get(new Integer(map));
		if (mapSrc == null) {
			//	_log.error("遊戲世界儲存中心並未建立該地圖編號資料檔案: " + map);
			return result;
		}

		for (final L1Object element : mapSrc.values()) {
			if (element.equals(src)) {
				continue;
			}
			final int key = (element.getX() << 16) + element.getY();
			if (lineMap.containsKey(key)) {
				result.add(element);
			}
		}
		lineMap.clear();
		return result;
	}

	/**
	 * 全部地圖(MAPID為KEY)世界資料
	 * @return
	 */
	public final HashMap<Integer, ConcurrentHashMap<Integer, L1Object>> getVisibleObjects() {
		return this._visibleObjects;
	}

	/**
	 * 指定地圖世界資料
	 * @param mapId
	 * @return
	 */
	public final ConcurrentHashMap<Integer, L1Object> getVisibleObjects(final int mapId) {
		return this._visibleObjects.get(new Integer(mapId));
	}
	/**
	 * 畫面可見範圍Objects
	 * @param object
	 * @return
	 */
	public ArrayList<L1Object> getVisibleObjects(final L1Object object) {
		return this.getVisibleObjects(object, -1);
	}


	/**
	 * 範圍物件
	 * @param object 原始物件
	 * @param radius 範圍 -1:全視窗 0:重疊 其他:範圍
	 * @return
	 */
	public ArrayList<L1Object> getVisibleObjects(final L1Object object, final int radius) {
		final L1Map map = object.getMap();
		final Point pt = object.getLocation();
		final ArrayList<L1Object> result = new ArrayList<L1Object>();
		// 取回原地圖資料
		final ConcurrentHashMap<Integer, L1Object> mapSrc =this._visibleObjects.get(new Integer(map.getId()));
		if (mapSrc == null) {
			//	_log.error("遊戲世界儲存中心並未建立該地圖編號資料檔案: " + map.getId());
			return result;
		}


		for (final L1Object element : mapSrc.values()) {
			if (element.equals(object)) {
				continue;
			}
			if (map != element.getMap()) {
				continue;
			}

			if (radius == -1) {
				if (pt.isInScreen(element.getLocation())) {
					result.add(element);
				}
			} else if (radius == 0) {
				if (pt.isSamePoint(element.getLocation())) {
					result.add(element);
				}
			} else {
				if (pt.getTileLineDistance(element.getLocation()) <= radius) {
					result.add(element);
				}
			}
		}

		return result;
	}
	
	/**
	 * 範圍物件
	 * @param object 原始物件
	 * @param radius 範圍 -1:全視窗 0:重疊 其他:範圍
	 * @return
	 */
	public L1MonsterInstance getVisibleMob(final L1Object object, final int radius) {
		final L1Map map = object.getMap();
		final Point pt = object.getLocation();
		L1MonsterInstance mob = null;
		// 取回原地圖資料
		final ConcurrentHashMap<Integer, L1Object> mapSrc =this._visibleObjects.get(new Integer(map.getId()));
		if (mapSrc == null) {
			//	_log.error("遊戲世界儲存中心並未建立該地圖編號資料檔案: " + map.getId());
			return null;
		}


		for (final L1Object element : mapSrc.values()) {
			if (element.equals(object)) {
				continue;
			}
			if (map != element.getMap()) {
				continue;
			}
			if(!(element instanceof L1MonsterInstance)){
				continue;
			}
				
			

			if (radius == -1) {
				if (pt.isInScreen(element.getLocation())) {
					mob = (L1MonsterInstance) element;
					break;
				}
			} else if (radius == 0) {
				if (pt.isSamePoint(element.getLocation())) {
					mob = (L1MonsterInstance) element;
					break;
				}
			} else {
				if (pt.getTileLineDistance(element.getLocation()) <= radius) {
					mob = (L1MonsterInstance) element;
					break;
				}
			}
		}

		return mob;
	}

	
	/**
	 * 取回地圖物件
	 * @param object 原始物件
	 * @param pc_only 是否只取回PC
	 * @return
	 */
	public ArrayList<L1Object> getAllVisibleObjectsMap(final L1Object object, final boolean pc_only) {
		final L1Map map = object.getMap();
		//		final Point pt = object.getLocation();
		final ArrayList<L1Object> result = new ArrayList<L1Object>();
		// 取回原地圖資料
		final ConcurrentHashMap<Integer, L1Object> mapSrc =this._visibleObjects.get(new Integer(map.getId()));
		if (mapSrc == null) {
			//	_log.error("遊戲世界儲存中心並未建立該地圖編號資料檔案: " + map.getId());
			return result;
		}

		for (final L1Object element : mapSrc.values()) {
			if (element.equals(object)) {
				continue;
			}
			if (map != element.getMap()) {
				continue;
			}
			if(pc_only && !(element instanceof L1PcInstance)){
				continue;
			}
			result.add(element);
		}

		return result;
	}
	/**
	 * 畫面可見人物
	 * @param object 原始執行物件
	 * @return
	 */
	public ArrayList<L1PcInstance> getVisiblePlayer(final L1Object object) {
		return this.getVisiblePlayer(object, -1);
	}

	/**
	 * 畫面可見人物
	 * @param object 原始執行物件
	 * @param radius -1:13格範圍 0:座標重疊 其它:指定範圍
	 * @return
	 */
	public ArrayList<L1PcInstance> getVisiblePlayer(final L1Object object, final int radius) {
		final int map = object.getMapId();
		final Point pt = object.getLocation();
		final ArrayList<L1PcInstance> result = new ArrayList<L1PcInstance>();

		for (final L1PcInstance element : this._allPlayers.values()) {
			if (element.equals(object)) {
				continue;
			}

			if (map != element.getMapId()) {
				continue;
			}
			switch(radius){
			case -1:
				if (pt.isInScreen(element.getLocation())) {
					result.add(element);
				}
				break;
			case 0:
				if (pt.isSamePoint(element.getLocation())) {
					result.add(element);
				}
				break;
			default:
				if (pt.getTileLineDistance(element.getLocation()) <= radius) {
					result.add(element);
				}
				break;
			}
		}
		return result;
	}

	/**
	 * 指定目標物件 獲取可見範圍PC物件
	 * @param object 執行者
	 * @param target 指定物件
	 * @return
	 */
	public ArrayList<L1PcInstance> getVisiblePlayerExceptTargetSight(final L1Object object, final L1Object target) {

		final int map = object.getMapId();
		final Point objectPt = object.getLocation();
		final Point targetPt = target.getLocation();
		final ArrayList<L1PcInstance> result = new ArrayList<L1PcInstance>();

		for (final L1PcInstance element : this._allPlayers.values()) {
			if (element.equals(object)) {
				continue;
			}

			if (map != element.getMapId()) {
				continue;
			}

			if (Config.PC_RECOGNIZE_RANGE == -1) {
				if (objectPt.isInScreen(element.getLocation())) {
					if (!targetPt.isInScreen(element.getLocation())) {
						result.add(element);
					}
				}
			} else {
				if (objectPt.getTileLineDistance(element.getLocation()) <= Config.PC_RECOGNIZE_RANGE) {
					if (targetPt.getTileLineDistance(element.getLocation()) > Config.PC_RECOGNIZE_RANGE) {
						result.add(element);
					}
				}
			}
		}
		return result;
	}

	/**
	 * 範圍物件(PC集體傳送使用)
	 * @param loc 原始座標
	 * @param radius 範圍
	 * @param showid 副本ID
	 * @return
	 */
	public ArrayList<L1Object> getVisiblePoint(final L1Location loc, final int radius) {
		final ArrayList<L1Object> result = new ArrayList<L1Object>();
		final int mapId = loc.getMapId(); // ループ内で呼ぶと重いため

		// 取回原地圖資料
		final ConcurrentHashMap<Integer, L1Object> mapSrc =
				this._visibleObjects.get(new Integer(mapId));
		if (mapSrc == null) {
			//	_log.error("遊戲世界儲存中心並未建立該地圖編號資料檔案: " + mapId);
			return result;
		}

		for (final L1Object element : mapSrc.values()) {
			if (mapId != element.getMapId()) {
				continue;
			}

			if (loc.getTileLineDistance(element.getLocation()) <= radius) {
				result.add(element);
			}
		}

		return result;
	}

	/**
	 * 取得世界天氣狀況
	 *
	 * @return
	 */
	public int getWeather() {
		return this._weather;
	}

	public boolean isHasObjects(L1Map map , Point pt){
		// 取回原地圖資料
		final ConcurrentHashMap<Integer, L1Object> mapSrc = this._visibleObjects.get(new Integer(map.getId()));
		if (mapSrc == null) {
			return true;
		}
		for (final L1Object element : mapSrc.values()) {
			if (map != element.getMap()) {
				continue;
			}
			if (pt.isSamePoint(element.getLocation())) {

				if(!(element instanceof L1Character)){
					continue;
				}
				final L1Character cha = (L1Character) element;
				if(cha.isDead()){continue;}
				if(cha instanceof L1PcInstance ){
					final L1PcInstance _pc =(L1PcInstance) cha;
					if (_pc.isGmInvis() || _pc.isGhost()|| _pc.hasSkillEffect(INVISIBILITY)) {
						continue;
					}
					return true;
				}else{
					//opqlo 修正穿人寫法
					if(!(cha instanceof L1DollInstance)
							&& !(cha instanceof L1EffectInstance)
							&& !(cha instanceof L1FieldObjectInstance)
							&& !(cha instanceof L1DoorInstance)){
						return true;
					}
					/*if( cha instanceof L1MonsterInstance){
						return true;
					}
					if (cha instanceof L1FieldObjectInstance){
						return true;
					}
					if(cha instanceof L1DoorInstance){
						return true;
					}*/
				}
			}
		}
		return false;
	}

	/**
	 * 計算貢獻度
	 * @return
	 */
	public boolean isProcessingContributionTotal() {
		return this._processingContributionTotal;
	}
	/**
	 * 允許廣播
	 *
	 * @return true允許 false不允許
	 */
	public boolean isWorldChatElabled() {
		return this._worldChatEnabled;
	}
	/**
	 * 移動MAP內物件
	 * @param object
	 * @param newMapId
	 */
	public void moveVisibleObject(final L1Object object, final int newMap){ // set_Mapで新しいMapにするまえに呼ぶこと

		final int srcMapId = object.getMapId();
		if (srcMapId != newMap) {
			// 取回原地圖資料
			final ConcurrentHashMap<Integer, L1Object> mapSrc =this._visibleObjects.get(new Integer(srcMapId));
			if (mapSrc != null) {
				mapSrc.remove(object.getId());
			}
			// 取回新地圖資料
			final ConcurrentHashMap<Integer, L1Object> map =this._visibleObjects.get(new Integer(newMap));
			if (map != null) {
				map.put(object.getId(), object);
			}
		}
	}
	/**
	 * 移出世界
	 * @param object
	 */
	public void removeObject(final L1Object object) {
		if (object == null) {
			throw new NullPointerException();
		}

		this._allObjects.remove(object.getId());

		if (object instanceof L1ItemInstance) {
			WorldItemEnchantTime.get().remove(new Integer(object.getId()));
			WorldItem.get().remove(new Integer(object.getId()));
		} else if (object instanceof L1PcInstance) {
			final L1PcInstance pc = (L1PcInstance) object;

			if (pc.isCrown()) {
				WorldCrown.get().remove(new Integer(pc.getId()));

			} else if (pc.isKnight()) {
				WorldKnight.get().remove(new Integer(pc.getId()));

			} else if (pc.isElf()) {
				WorldElf.get().remove(new Integer(pc.getId()));

			} else if (pc.isWizard()) {
				WorldWizard.get().remove(new Integer(pc.getId()));

			} else if (pc.isDarkelf()) {
				WorldDarkelf.get().remove(new Integer(pc.getId()));

			} else if (pc.isDragonKnight()) {
				WorldDragonKnight.get().remove(new Integer(pc.getId()));

			} else if (pc.isIllusionist()) {
				WorldIllusionist.get().remove(new Integer(pc.getId()));
			}
			if(pc.isGm()){
				this._allGms.remove(pc.getId());
			}
			this._allPlayers.remove(pc.getName());
		}else{
			// 寵物
			if (object instanceof L1PetInstance) {
				WorldPet.get().remove(new Integer(object.getId()));
			}
			// 召喚獸
			if (object instanceof L1SummonInstance) {
				WorldSummons.get().remove(new Integer(object.getId()));
			}
			// MOB
			if (object instanceof L1MonsterInstance) {
				WorldMob.get().remove(new Integer(object.getId()));
			}
			// NPC
			if (object instanceof L1NpcInstance) {
				WorldNpc.get().remove(new Integer(object.getId()));
			}
		}
	}

	/**
	 * 移除交易背包
	 * */
	public void removeTradeInventory(final int pc_objid){
		this._trade_inventory.remove(pc_objid);
	}

	/**
	 * 移除MAP內物件
	 * @param object
	 */
	public void removeVisibleObject(final L1Object object) {
		final ConcurrentHashMap<Integer, L1Object> map = this._visibleObjects.get(new Integer(object.getMapId()));
		if (map != null) {
			map.remove(object.getId());
		} else {
			//	_log.error("遊戲世界儲存中心並未建立該地圖編號資料檔案: " + object.getMapId());
		}
	}
	/**
	 * 允許廣播
	 *
	 * @param flag
	 */
	public void set_worldChatElabled(final boolean flag) {
		this._worldChatEnabled = flag;
	}

	/**
	 * 計算貢獻度
	 * @param flag
	 */
	public void setProcessingContributionTotal(final boolean flag) {
		this._processingContributionTotal = flag;
	}

	/**
	 * 設定世界天氣狀況
	 *
	 * @param weather
	 */
	public void setWeather(final int weather) {
		this._weather = weather;
	}
	/**
	 * 加入世界
	 * @param object
	 */
	public void storeObject(final L1Object object) {
		if (object == null) {
			throw new NullPointerException();
		}

		this._allObjects.put(object.getId(), object);

		/*	if (object instanceof L1ItemInstance) {
			WorldItem.get().put(new Integer(object.getId()), (L1ItemInstance) object);
		}*/
		if (object instanceof L1PcInstance) {
			final L1PcInstance pc = (L1PcInstance) object;

			if (pc.isCrown()) {
				WorldCrown.get().put(new Integer(pc.getId()), pc);

			} else if (pc.isKnight()) {
				WorldKnight.get().put(new Integer(pc.getId()), pc);

			} else if (pc.isElf()) {
				WorldElf.get().put(new Integer(pc.getId()), pc);

			} else if (pc.isWizard()) {
				WorldWizard.get().put(new Integer(pc.getId()), pc);

			} else if (pc.isDarkelf()) {
				WorldDarkelf.get().put(new Integer(pc.getId()), pc);

			} else if (pc.isDragonKnight()) {
				WorldDragonKnight.get().put(new Integer(pc.getId()), pc);

			} else if (pc.isIllusionist()) {
				WorldIllusionist.get().put(new Integer(pc.getId()), pc);
			}
			if(pc.isGm()){
				this._allGms.put(pc.getId(), pc);
			}

			this._allPlayers.put(pc.getName(), pc);


		}else if (object instanceof L1ItemInstance){
			if(((L1ItemInstance)object).getDeleteDate() !=null){
				WorldItem.get().put(new Integer(object.getId()), (L1ItemInstance) object);
			}

		}else{
			// 召喚獸
			if (object instanceof L1SummonInstance) {
				WorldSummons.get().put(new Integer(object.getId()), (L1SummonInstance) object);
			}
			// 寵物
			if (object instanceof L1PetInstance) {
				WorldPet.get().put(new Integer(object.getId()), (L1PetInstance) object);
			}
			// MOB
			if (object instanceof L1MonsterInstance) {
				WorldMob.get().put(new Integer(object.getId()), (L1MonsterInstance) object);
			}
			// NPC
			if (object instanceof L1NpcInstance) {
				WorldNpc.get().put(new Integer(object.getId()), (L1NpcInstance) object);
			}
			// 魔法娃娃
			/*	if (object instanceof L1DollInstance) {
			WorldDoll.get().put(new Integer(object.getId()), (L1DollInstance) object);
		}
		// Effect(技能物件)
		if (object instanceof L1EffectInstance) {
			WorldEffect.get().put(new Integer(object.getId()), (L1EffectInstance) object);
		}*/
		}
	}

	public int getAllPlayerSize(){
		return _allPlayers.size();
	}


	/**
	 * 取得灌水機器人人數
	 * 
	 * @return the _bogusAmount
	 */
	public int getBogusAmount()
	{
		return  _dummyAmount;
	}

	/**
	 * 假人人數遞增
	 * 
	 * @param bogusAmount the _bogusAmount to set
	 */
	public void addDummyAmount()
	{
		_dummyAmount++;
	}

	public void clearDummyAmount(){
		_dummyAmount =0;
	}

	/**
	 * @return the _npcshop
	 */
	public List<L1DummyInstance> getDummyList()
	{
		return _dummy;
	}

}